namespace Angelus.Data.Linq.ProviderBase
{
	using System;
    using System.Collections.Generic;
    using System.Data.DLinq.ProviderBase;
    using System.Text;

    /// <summary>
    /// 
    /// </summary>
    internal class OracleFormatter : DbFormatter
    {
        #region Fields

        private readonly Visitor _visitor;

        #endregion

        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        public OracleFormatter()
        {
            this._visitor = new Visitor();
        }

        #endregion

        #region DbFormatter Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        internal override string[] Format(SqlNode node)
        {
            return this._visitor.Format(node);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <param name="showProjection"></param>
        /// <returns></returns>
        internal override string[] Format(SqlNode node, bool showProjection)
        {
            return this._visitor.Format(node, showProjection);
        }

        #endregion

        #region Nested classes

        /// <summary>
        /// 
        /// </summary>
        internal class Visitor : SqlVisitor
        {
            #region Fields

            private readonly Dictionary<SqlColumn, SqlAlias> _aliases;
            private readonly Dictionary<SqlNode, string> _names;
            private readonly List<string> _queries;
            private readonly List<SqlSource> _suppressedAliases;
            private StringBuilder _builder;
            private SqlAlias _currentAlias;
            private int _depth;
            private bool _showProjection;

            //private int nDepth;

            #endregion

            #region Constructors

            /// <summary>
            /// 
            /// </summary>
            internal Visitor()
            {
                this._queries = new List<string>();
                this._suppressedAliases = new List<SqlSource>();
                this._names = new Dictionary<SqlNode, string>();
                this._aliases = new Dictionary<SqlColumn, SqlAlias>();
            }

            #endregion

            #region Visiting Implementation

            /// <summary>
            /// 
            /// </summary>
            /// <param name="alias"></param>
            /// <returns></returns>
            internal override SqlAlias VisitAlias(SqlAlias alias)
            {
                SqlAlias current = this._currentAlias;
                this._currentAlias = alias;
                bool flag2 = alias.Node is SqlSelect;
                int num2 = this._depth;
                string text3 = null;
                string text4 = string.Empty;
                SqlTable table2 = alias.Node as SqlTable;
                if (table2 != null)
                {
                    text4 = table2.Name;
                }
                if (alias.Name == null)
                {
                    if (!this._names.TryGetValue(alias, out text3))
                    {
                        text3 = "A" + this._names.Count;
                        this._names[alias] = text3;
                    }
                }
                else
                {
                    text3 = alias.Name;
                }
                if (flag2)
                {
                    this._depth++;
                    this._builder.Append("(");
                    this.NewLine();
                }
                Visit(alias.Node);
                if (flag2)
                {
                    this.NewLine();
                    this._builder.Append(")");
                    this._depth = num2;
                }
                if ((!this._suppressedAliases.Contains(alias) && (text3 != null)) && (text4 != text3))
                {
                    this._builder.Append(" ");
                    this.WriteName(text3);
                }
                this._currentAlias = current;
                return alias;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="reference"></param>
            /// <returns></returns>
            internal override SqlExpression VisitAliasRef(SqlAliasRef reference)
            {
                string text = null;
                if (null == reference.Alias.Name)
                {
                    if (!this._names.TryGetValue(reference.Alias, out text))
                    {
                        text = "A" + this._names.Count;
                        this._names[reference.Alias] = text;
                    }
                }
                else
                {
                    text = reference.Alias.Name;
                }
                this._builder.Append(text);
                return reference;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="assign"></param>
            /// <returns></returns>
            internal override SqlStatement VisitAssign(SqlAssign assign)
            {
                this._builder.Append("SET ");
                Visit(assign.LValue);
                this._builder.Append(" = ");
                Visit(assign.RValue);
                return assign;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="binary"></param>
            /// <returns></returns>
            internal override SqlExpression VisitBinaryOperator(SqlBinary binary)
            {
                if (SqlNodeType.Coalesce == binary.NodeType)
                {
                    this._builder.Append("COALESCE(");
                    Visit(binary.Left);
                    this._builder.Append(",");
                    Visit(binary.Right);
                    this._builder.Append(")");
                    return binary;
                }

                this.VisitWithParens(binary.Left, binary);
                this._builder.Append(" ");
                this._builder.Append(this.GetOperator(binary.NodeType));
                this._builder.Append(" ");
                this.VisitWithParens(binary.Right, binary);
                return binary;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="block"></param>
            /// <returns></returns>
            internal override SqlBlock VisitBlock(SqlBlock block)
            {
                if (block.Statements.Count > 0)
                {
                    this._builder.Append("BEGIN ");
                }

                for (int index = 0, max = block.Statements.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append("; ");
                    }
                    Visit(block.Statements[index]);
                }
                this._builder.Append("; ");

                if (block.Statements.Count > 0)
                {
                    this._builder.Append("END;");
                }

                return block;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="cast"></param>
            /// <returns></returns>
            internal override SqlExpression VisitCast(SqlUnary cast)
            {
                this._builder.Append("CAST(");
                Visit(cast.Operand);
                this._builder.Append(" AS ");
                this._builder.Append(cast.SqlType.ToQueryString(QueryFormatOptions.SuppressSize));
                this._builder.Append(")");
                return cast;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="array"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientArray(SqlClientArray array)
            {
                this._builder.Append("new [] {");
                for (int index = 0, max = array.Expressions.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    Visit(array.Expressions[index]);
                }
                this._builder.Append("}");
                return array;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="client"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientCase(SqlClientCase client)
            {
                this._depth++;
                this.NewLine();
                this._builder.Append("(CASE");
                this._depth++;

                if (null != client.Expression)
                {
                    this._builder.Append(" ");
                    Visit(client.Expression);
                }

                for (int index = 0, max = client.Whens.Count; index < max; index++)
                {
                    SqlClientWhen when = client.Whens[index];
                    if ((index == (max - 1)) && (null == when.Match))
                    {
                        this.NewLine();
                        this._builder.Append(" ELSE ");
                        Visit(when.Value);
                    }
                    else
                    {
                        this.NewLine();
                        this._builder.Append(" WHEN ");
                        Visit(when.Match);
                        this._builder.Append(" THEN ");
                        Visit(when.Value);
                    }
                }
                this._depth--;
                this.NewLine();
                this._builder.Append(" END ");
                this._depth--;
                return client;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="query"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientCollection(SqlClientQuery query)
            {
                if (null != query.Count)
                {
                    Visit(query.Count);
                    return query;
                }
                this._builder.Append("query(");
                this._builder.Append(query.Index);
                this._builder.Append(")");
                return query;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="query"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientElement(SqlClientQuery query)
            {
                return this.VisitClientCollection(query);
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="expression"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientExpression(SqlUnary expression)
            {
                string text = null;
                if (!this._names.TryGetValue(expression, out text))
                {
                    text = "@P" + this._names.Count;
                    this._names.Add(expression, text);
                }
                this._builder.Append(text);
                return expression;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="parameter"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientParameter(SqlClientParameter parameter)
            {
                if (!this._showProjection)
                {
                    throw new NotImplementedException("ClientParameter");
                }
                return parameter;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="query"></param>
            /// <returns></returns>
            internal override SqlExpression VisitClientQuery(SqlClientQuery query)
            {
                Visit(query.Count);
                return query;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="column"></param>
            /// <returns></returns>
            internal override SqlExpression VisitColumn(SqlColumn column)
            {
                this._builder.Append("Column(");
                if ((null == column.Alias) && !this._aliases.ContainsKey(column))
                {
                    this._aliases.Add(column, this._currentAlias);
                }
                Visit(column.Expression);
                this._builder.Append(")");
                return column;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="columnRef"></param>
            /// <returns></returns>
            internal override SqlExpression VisitColumnRef(SqlColumnRef cref)
            {
                string text4 = null;
                SqlColumn column2 = cref.Column;
                SqlAlias alias2 = column2.Alias;
                if (alias2 == null)
                {
                    this._aliases.TryGetValue(column2, out alias2);
                }
                if (alias2 != null)
                {
                    if (alias2.Name == null)
                    {
                        if (!this._names.TryGetValue(alias2, out text4))
                        {
                            text4 = "A" + this._names.Count;
                            this._names[column2.Alias] = text4;
                        }
                    }
                    else
                    {
                        text4 = column2.Alias.Name;
                    }
                }
                if ((!this._suppressedAliases.Contains(column2.Alias) && (text4 != null)) && (text4.Length != 0))
                {
                    this.WriteName(text4);
                    this._builder.Append(".");
                }
                string text5 = column2.Name;
                string text6 = this.InferName(column2.Expression, null);
                if (text5 == null)
                {
                    text5 = text6;
                }
                if ((text5 == null) && !this._names.TryGetValue(column2, out text5))
                {
                    text5 = "C" + this._names.Count;
                    this._names[column2] = text5;
                }
                this.WriteName(text5);
                return cref;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="cursor"></param>
            /// <returns></returns>
            internal override SqlStatement VisitCursor(SqlCursor cursor)
            {
                if (cursor.Locals.Columns.Count > 0)
                {
                    StringBuilder builder = this._builder;
                    this._builder = new StringBuilder();
                    this._builder.Append("DECLARE ");
                    int num1 = 0;
                    int num2 = cursor.Locals.Columns.Count;
                    while (num1 < num2)
                    {
                        if (num1 > 0)
                        {
                            this._builder.Append(", ");
                        }
                        SqlColumn column1 = cursor.Locals.Columns[num1];
                        this._builder.Append(column1.Name);
                        this._builder.Append(" ");
                        this.FormatType(column1.SqlType);
                        num1++;
                    }
                    this._builder.Append(";");
                    this._builder.Append(builder.ToString());
                }
                if (null != (cursor as OracleRefCursor).UserQuery)
                {
                    this._builder.Append((cursor as OracleRefCursor).UserQuery.QueryText);
                }

                this._builder.Append("OPEN ");
                this.VisitParameter((cursor as OracleRefCursor).Parameter);
                this._builder.Append(" FOR ");
                Visit(cursor.Select);
                return cursor;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="delete"></param>
            /// <returns></returns>
            internal override SqlStatement VisitDelete(SqlDelete delete)
            {
                this._builder.Append("DELETE FROM ");
                this._suppressedAliases.Add(delete.Select.From);
                Visit(delete.Select.From);
                if (null != delete.Select.Where)
                {
                    this._builder.Append(" WHERE ");
                    Visit(delete.Select.Where);
                }
                this._suppressedAliases.Remove(delete.Select.From);
                return delete;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="element"></param>
            /// <returns></returns>
            internal override SqlExpression VisitElement(SqlSubSelect element)
            {
                int savedDepth = this._depth;
                this._depth++;
                this._builder.Append("ELEMENT(");
                this.NewLine();
                Visit(element.Select);
                this.NewLine();
                this._builder.Append(")");
                this._depth = savedDepth;
                return element;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="element"></param>
            /// <returns></returns>
            internal override SqlExpression VisitExists(SqlSubSelect element)
            {
                int savedDepth = this._depth;
                this._depth++;
                this._builder.Append("EXISTS ( ");
                this.NewLine();
                Visit(element.Select);
                this.NewLine();
                this._builder.Append(" ) ");
                this._depth = savedDepth;
                return element;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="set"></param>
            /// <returns></returns>
            internal override SqlExpression VisitExprSet(SqlExprSet set)
            {
                if (this._showProjection)
                {
                    this._builder.Append("ES ( ");
                    for (int index = 0, max = set.Expressions.Count; index < max; index++)
                    {
                        if (index > 0)
                        {
                            this._builder.Append(", ");
                        }
                        Visit(set.Expressions[index]);
                    }
                    this._builder.Append(" ) ");
                    return set;
                }
                Visit(set.Expressions[0]);
                return set;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="function"></param>
            /// <returns></returns>
            internal override SqlExpression VisitFunctionCall(SqlFunctionCall function)
            {
                this._builder.Append(function.Name);
                this._builder.Append(" ( ");

                for (int index = 0, max = function.Arguments.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    Visit(function.Arguments[index]);
                }
                this._builder.Append(" ) ");
                return function;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="grouping"></param>
            /// <returns></returns>
            internal override SqlExpression VisitGrouping(SqlGrouping grouping)
            {
                if (!this._showProjection)
                {
                    throw new NotImplementedException("Grouping");
                }
                this._builder.Append("Group ( ");
                Visit(grouping.Key);
                this._builder.Append(", ");
                Visit(grouping.Group);
                this._builder.Append(" ) ");
                return grouping;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="including"></param>
            /// <returns></returns>
            internal override SqlExpression VisitIncluding(SqlIncluding including)
            {
                Visit(including.Expression);
                return including;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="insert"></param>
            /// <returns></returns>
            internal override SqlStatement VisitInsert(SqlInsert insert)
            {
                this._builder.Append("INSERT INTO ");
                Visit(insert.Table);
                this._builder.Append(" ( ");

                for (int index = 0, max = insert.Row.Columns.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    this._builder.Append(insert.Row.Columns[index].Name);
                }

                this._builder.Append(" ) VALUES ( ");
                for (int index = 0, max = insert.Row.Columns.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    Visit(insert.Row.Columns[index].Expression);
                }
                this._builder.Append(" ) ");
                return insert;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="join"></param>
            /// <returns></returns>
            internal override SqlSource VisitJoin(SqlJoin join)
            {
                Visit(join.Left);
                this.NewLine();
                switch (join.JoinType)
                {
                    case SqlJoinType.Cross:
                        this._builder.Append("CROSS JOIN ");
                        break;

                    case SqlJoinType.FullOuter:
                        this._builder.Append("FULL OUTER JOIN ");
                        break;

                    case SqlJoinType.Inner:
                        this._builder.Append("INNER JOIN ");
                        break;

                    case SqlJoinType.LeftOuter:
                        this._builder.Append("LEFT OUTER JOIN ");
                        break;

                    case SqlJoinType.RightOuter:
                        this._builder.Append("RIGHT OUTER JOIN ");
                        break;
                }
                SqlJoin rightJoin = join.Right as SqlJoin;
                if ((null == rightJoin) || (SqlJoinType.Cross == join.JoinType))
                {
                    Visit(join.Right);
                }
                else
                {
                    this.VisitJoinSource(join.Right);
                }
                if (null != join.Condition)
                {
                    this._builder.Append(" ON ");
                    Visit(join.Condition);
                    return join;
                }
                if (this.RequiresOnCondition(join.JoinType))
                {
                    this._builder.Append(" ON 1=1 ");
                }
                return join;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="like"></param>
            /// <returns></returns>
            internal override SqlExpression VisitLike(SqlLike like)
            {
                this.VisitWithParens(like.Expression, like);
                this._builder.Append(" LIKE ");
                Visit(like.Pattern);
                if (null != like.Escape)
                {
                    this._builder.Append(" ESCAPE ");
                    Visit(like.Escape);
                }
                return like;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="link"></param>
            /// <returns></returns>
            internal override SqlNode VisitLink(SqlLink link)
            {
                if (null != link.Expansion)
                {
                    this._builder.Append("LINK(");
                    Visit(link.Expansion);
                    this._builder.Append(")");
                    return link;
                }
                this._builder.Append("LINK(");
                for (int index = 0, max = link.Expressions.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    Visit(link.Expressions[index]);
                }
                this._builder.Append(")");
                return link;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="literal"></param>
            /// <returns></returns>
            internal override SqlExpression VisitLiteral(SqlLiteral literal)
            {
                if (literal.SqlType.IsApplicationTypeOf(0))
                {
                    if ((bool) literal.Value)
                    {
                        this._builder.Append("1=1");
                        return literal;
                    }
                    this._builder.Append("1=0");
                    return literal;
                }
                this.FormatValue(literal.Value);
                return literal;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="member"></param>
            /// <returns></returns>
            internal override SqlNode VisitMember(SqlMember member)
            {
                Visit(member.Expression);
                this._builder.Append(".");
                this._builder.Append(member.Member.Name);
                return member;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="member"></param>
            /// <returns></returns>
            internal override SqlMemberAssign VisitMemberAssign(SqlMemberAssign member)
            {
                throw new NotImplementedException("MemberAssign");
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="method"></param>
            /// <returns></returns>
            internal override SqlExpression VisitMethodCall(SqlMethodCall method)
            {
                if (method.Method.IsStatic)
                {
                    this._builder.Append(method.Method.DeclaringType);
                }
                else
                {
                    Visit(method.Object);
                }
                this._builder.Append(".");
                this._builder.Append(method.Method.Name);
                this._builder.Append("(");

                for (int index = 0, max = method.Arguments.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    Visit(method.Arguments[index]);
                }
                this._builder.Append(")");
                return method;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="select"></param>
            /// <returns></returns>
            internal override SqlExpression VisitMultiset(SqlSubSelect select)
            {
                int savedDepth = this._depth;
                this._depth++;
                this._builder.Append("MULTISET(");
                this.NewLine();
                Visit(select.Select);
                this.NewLine();
                this._builder.Append(")");
                this._depth = savedDepth;
                return select;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="creator"></param>
            /// <returns></returns>
            internal override SqlExpression VisitNew(SqlNew creator)
            {
                this._builder.Append("new ");
                this._builder.Append(creator.ClrType.Name);
                this._builder.Append("{ ");

                for (int index = 0, max = creator.Members.Count; index < max; index++)
                {
                    SqlMemberAssign assign = creator.Members[index];
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    string name = this.InferName(assign.Expression, null);
                    if (name != assign.Member.Name)
                    {
                        this._builder.Append(assign.Member.Name);
                        this._builder.Append(" = ");
                    }
                    Visit(assign.Expression);
                }
                this._builder.Append("}");
                return creator;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            internal override SqlExpression VisitOptionalValue(SqlOptionalValue value)
            {
                if (this._showProjection)
                {
                    this._builder.Append("opt(");
                    Visit(value.HasValue);
                    this._builder.Append(", ");
                    Visit(value.Value);
                    this._builder.Append(")");
                    return value;
                }
                throw new NotImplementedException("OptionalValue");
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="p"></param>
            /// <returns></returns>
            internal override SqlExpression VisitParameter(SqlParameter p)
            {
                this._builder.Append(p.Name);
                return p;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="set"></param>
            /// <returns></returns>
            internal override SqlQuerySet VisitQuerySet(SqlQuerySet set)
            {
                for (int index = 0, max = set.Queries.Count; index < max; index++)
                {
                    this._builder.Length = 0;
                    Visit(set.Queries[index]);
                    this._queries.Add(this._builder.ToString());
                }
                return set;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="row"></param>
            /// <returns></returns>
            internal override SqlRow VisitRow(SqlRow row)
            {
                for (int index = 0, max = row.Columns.Count; index < max; index++)
                {
                    SqlColumn column = row.Columns[index];
                    if ((null == column.Alias) && !this._aliases.ContainsKey(column))
                    {
                        this._aliases.Add(column, this._currentAlias);
                    }
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    Visit(column.Expression);
                    string name = column.Name;
                    string inferName = this.InferName(column.Expression, null);
                    if (null == name)
                    {
                        name = inferName;
                    }
                    if ((null == name) && !this._names.TryGetValue(column, out name))
                    {
                        name = "C" + this._names.Count;
                        this._names[column] = name;
                    }
                    if ((name != inferName) && !string.IsNullOrEmpty(name))
                    {
                        this._builder.Append(" AS ");
                        this.WriteName(name);
                    }
                }
                return row;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="select"></param>
            /// <returns></returns>
            internal override SqlExpression VisitScalarSubSelect(SqlSubSelect select)
            {
                int savedDepth = this._depth;
                this._depth++;
                this._builder.Append("(");
                this.NewLine();
                Visit(select.Select);
                this.NewLine();
                this._builder.Append(")");
                this._depth = savedDepth;
                return select;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="searchedCase"></param>
            /// <returns></returns>
            internal override SqlExpression VisitSearchedCase(SqlSearchedCase searchedCase)
            {
                this._depth++;
                this.NewLine();
                this._builder.Append("(CASE ");
                this._depth++;

                for (int index = 0, max = searchedCase.Whens.Count; index < max; index++)
                {
                    SqlWhen when = searchedCase.Whens[index];
                    this.NewLine();
                    this._builder.Append("WHEN ");
                    Visit(when.Match);
                    this._builder.Append(" THEN ");
                    Visit(when.Value);
                }

                if (null != searchedCase.Else)
                {
                    this.NewLine();
                    this._builder.Append(" ELSE ");
                    Visit(searchedCase.Else);
                }
                this._depth--;
                this.NewLine();
                this._builder.Append(" END)");
                this._depth--;

                return searchedCase;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="select"></param>
            /// <returns></returns>
            internal override SqlSelect VisitSelect(SqlSelect select)
            {
                string queryText = null;
                if (null != select.From)
                {
                    StringBuilder builder = this._builder;
                    this._builder = new StringBuilder();
                    if (this.IsSimpleCrossJoinList(select.From))
                    {
                        this.VisitCrossJoinList(select.From);
                    }
                    else
                    {
                        Visit(select.From);
                    }
                    queryText = this._builder.ToString();
                    this._builder = builder;
                }

                this._builder.Append("SELECT ");

                if (select.IsDistinct)
                {
                    this._builder.Append("DISTINCT ");
                }
                if (select.Row.Columns.Count > 0)
                {
                    this.VisitRow(select.Row);
                }
                else if (this._showProjection)
                {
                    Visit(select.Selection);
                }
                else
                {
                    //this._builder.Append("NULL AS [EMPTY]");
                    this._builder.Append("*");
                }

                if (null != queryText)
                {
                    this.NewLine();
                    this._builder.Append("FROM ");
                    this._builder.Append(queryText);
                }

                if (null != select.Where)
                {
                    this.NewLine();
                    this._builder.Append("WHERE ");
                    Visit(select.Where);
                }

                if (select.GroupBy.Count > 0)
                {
                    this.NewLine();
                    this._builder.Append("GROUP BY ");
                    for (int index = 0, max = select.GroupBy.Count; index < max; index++)
                    {
                        SqlExpression expression = select.GroupBy[index];
                        if (index > 0)
                        {
                            this._builder.Append(", ");
                        }
                        Visit(expression);
                    }

                    if (null != select.Having)
                    {
                        this.NewLine();
                        this._builder.Append("HAVING ");
                        Visit(select.Having);
                    }
                }

                if ((select.OrderBy.Count > 0) && (SqlOrderingType.Never != select.OrderingType))
                {
                    this.NewLine();
                    this._builder.Append("ORDER BY ");

                    for (int index = 0, max = select.OrderBy.Count; index < max; index++)
                    {
                        SqlOrderExpression expression = select.OrderBy[index];
                        if (index > 0)
                        {
                            this._builder.Append(", ");
                        }
                        Visit(expression.Expression);
                        if (SqlOrderType.Descending == expression.OrderType)
                        {
                            this._builder.Append(" DESC");
                        }
                    }
                }

                if (select.Top != null)
                {
                    string selection = "SELECT * FROM ";
                    int pos = (this._depth > 0) ? 0 : selection.Length;
                    this._builder.Insert(0, selection).Insert(pos, " ( ").Append(") WHERE ROWNUM <= ");
                    this.Visit(select.Top);
                    this._builder.Append(" ");
                }

                return select;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="simpleCase"></param>
            /// <returns></returns>
            internal override SqlExpression VisitSimpleCase(SqlSimpleCase simpleCase)
            {
                throw new NotImplementedException("SearchedCase");
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="table"></param>
            /// <returns></returns>
            internal override SqlTable VisitTable(SqlTable table)
            {
                string name = table.Name;
                this._builder.Append(name);
                return table;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="function"></param>
            /// <returns></returns>
            internal override SqlExpression VisitTableValuedFunctionCall(SqlTableValuedFunctionCall function)
            {
                throw new NotImplementedException("TableValuedFunctionCall");
            }

            /// <summary>
            /// /
            /// </summary>
            /// <param name="update"></param>
            /// <returns></returns>
            internal override SqlStatement VisitUpdate(SqlUpdate update)
            {
                this._builder.Append("UPDATE ");
                this._suppressedAliases.Add(update.Select.From);
                Visit(update.Select.From);
                this.NewLine();
                this._builder.Append("SET ");

                for (int index = 0, max = update.Assignments.Count; index < max; index++)
                {
                    if (index > 0)
                    {
                        this._builder.Append(", ");
                    }
                    SqlAssign assign = update.Assignments[index];
                    Visit(assign.LValue);
                    this._builder.Append(" = ");
                    Visit(assign.RValue);
                }

                if (null != update.Select.Where)
                {
                    this.NewLine();
                    this._builder.Append("WHERE ");
                    Visit(update.Select.Where);
                }
                this._suppressedAliases.Remove(update.Select.From);
                return update;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="uo"></param>
            /// <returns></returns>
            internal override SqlExpression VisitUnaryOperator(SqlUnary uo)
            {
                SqlNodeType type1 = uo.NodeType;
                if (type1 <= SqlNodeType.LongCount)
                {
                    switch (type1)
                    {
                        case SqlNodeType.Convert:
                            this._builder.Append("CONVERT(");
                            this._builder.Append(uo.SqlType.ToQueryString(QueryFormatOptions.SuppressSize));
                            this._builder.Append(",");
                            Visit(uo.Operand);
                            this._builder.Append(")");
                            return uo;

                        case SqlNodeType.Count:
                        case SqlNodeType.Covar:
                        case SqlNodeType.LongCount:
                        case SqlNodeType.Avg:
                            goto Label_0101;

                        case SqlNodeType.IsNotNull:
                        case SqlNodeType.IsNull:
                            this.VisitWithParens(uo.Operand, uo);
                            this._builder.Append(" ");
                            this._builder.Append(this.GetOperator(uo.NodeType));
                            return uo;

                        case SqlNodeType.BitNot:
                            goto Label_00D7;
                    }
                    goto Label_020C;
                }
                if (type1 <= SqlNodeType.Not)
                {
                    switch (type1)
                    {
                        case SqlNodeType.Negate:
                            goto Label_00D7;

                        case SqlNodeType.New:
                            goto Label_020C;

                        case SqlNodeType.Not:
                            this._builder.Append(this.GetOperator(uo.NodeType));
                            this._builder.Append(" ");
                            this.VisitWithParens(uo.Operand, uo);
                            return uo;

                        case SqlNodeType.Min:
                        case SqlNodeType.Max:
                            goto Label_0101;
                    }
                    goto Label_020C;
                }
                switch (type1)
                {
                    case SqlNodeType.Stddev:
                    case SqlNodeType.Sum:
                        goto Label_0101;

                    default:
                        if (type1 == SqlNodeType.ValueOf)
                        {
                            Visit(uo.Operand);
                            return uo;
                        }
                        goto Label_020C;
                }
                Label_00D7:
                this._builder.Append(this.GetOperator(uo.NodeType));
                this.VisitWithParens(uo.Operand, uo);
                return uo;
                Label_0101:
                this._builder.Append(this.GetOperator(uo.NodeType));
                this._builder.Append("(");
                if (uo.Operand == null)
                {
                    this._builder.Append("*");
                }
                else
                {
                    Visit(uo.Operand);
                }
                this._builder.Append(")");
                return uo;
                Label_020C:
                throw new Exception("Unrecognized unary operator type: " + uo.NodeType);
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="su"></param>
            /// <returns></returns>
            internal override SqlNode VisitUnion(SqlUnion su)
            {
                this._builder.Append("(");
                int depth = this._depth;
                this._depth++;
                this.NewLine();
                Visit(su.Left);
                this.NewLine();
                this._builder.Append("UNION");
                if (su.All)
                {
                    this._builder.Append(" ALL");
                }
                this.NewLine();
                Visit(su.Right);
                this.NewLine();
                this._builder.Append(")");
                this._depth = depth;
                return su;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="variable"></param>
            /// <returns></returns>
            internal override SqlExpression VisitVariable(SqlVariable variable)
            {
                this._builder.Append(variable.Name);
                return variable;
            }

            #endregion

            #region Formatting Implementation

            /// <summary>
            /// 
            /// </summary>
            /// <param name="escape"></param>
            /// <returns></returns>
            internal virtual string EscapeSingleQuotes(string escape)
            {
                if (escape.IndexOf('\'') < 0)
                {
                    return escape;
                }
                StringBuilder builder = new StringBuilder();
                foreach (char item in escape)
                {
                    if ('\'' == item)
                    {
                        builder.Append("''");
                    }
                    else
                    {
                        builder.Append("'");
                    }
                }
                return builder.ToString();
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="node"></param>
            /// <returns></returns>
            internal string[] Format(SqlNode node)
            {
                return this.Format(node, false);
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="node"></param>
            /// <param name="showProjection"></param>
            /// <returns></returns>
            internal string[] Format(SqlNode node, bool showProjection)
            {
                this._builder = new StringBuilder();
                this._showProjection = showProjection;
                this._queries.Clear();
                this._aliases.Clear();
                Visit(node);
                if (0 == this._queries.Count)
                {
                    this._queries.Add(this._builder.ToString());
                }
                return this._queries.ToArray();
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="value"></param>
            internal virtual void FormatValue(object value)
            {
                if (null == value)
                {
                    this._builder.Append("NULL");
                }
                else
                {
                    Type type = value.GetType();
                    if (type.IsGenericType && (typeof (Nullable<>) == type.GetGenericTypeDefinition()))
                    {
                        type = type.GetGenericArguments()[0];
                    }
                    switch (Type.GetTypeCode(type))
                    {
                        case TypeCode.Object:
                            if (value is Guid)
                            {
                                this._builder.Append("'");
                                this._builder.Append(value);
                                this._builder.Append("'");
                                return;
                            }
                            Type valueType = value as Type;
                            if (null != valueType)
                            {
                                this._builder.Append("typeof(");
                                this._builder.Append(valueType.Name);
                                this._builder.Append(")");
                                return;
                            }
                            break;

                        case TypeCode.Boolean:
                            this._builder.Append(((bool) value) ? "1" : "0");
                            return;

                        case TypeCode.Char:
                        case TypeCode.DateTime:
                        case TypeCode.String:
                            this._builder.Append("'");
                            this._builder.Append(this.EscapeSingleQuotes(value.ToString()));
                            this._builder.Append("'");
                            return;

                        case TypeCode.SByte:
                        case TypeCode.Byte:
                        case TypeCode.Int16:
                        case TypeCode.UInt16:
                        case TypeCode.Int32:
                        case TypeCode.UInt32:
                        case TypeCode.Int64:
                        case TypeCode.UInt64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            this._builder.Append(value);
                            return;

                        default:
                            throw new Exception("Value has no literal in SQL: " + value);
                    }
                }
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="nodeType"></param>
            /// <returns></returns>
            internal virtual string GetOperator(SqlNodeType nodeType)
            {
                switch (nodeType)
                {
                    case SqlNodeType.Add:
                        return "+";

                    case SqlNodeType.And:
                        return "AND";

                    case SqlNodeType.Avg:
                        return "AVG";

                    case SqlNodeType.BitAnd:
                        return "&";

                    case SqlNodeType.BitNot:
                        return "~";

                    case SqlNodeType.BitOr:
                        return "|";

                    case SqlNodeType.BitXor:
                        return "^";

                    case SqlNodeType.Concat:
                        return "||";

                    case SqlNodeType.Count:
                        return "COUNT";

                    case SqlNodeType.Covar:
                        return "COVAR";

                    case SqlNodeType.Div:
                        return "/";

                    case SqlNodeType.EQ:
                        return "=";

                    case SqlNodeType.IsNotNull:
                        return "IS NOT NULL";

                    case SqlNodeType.IsNull:
                        return "IS NULL";

                    case SqlNodeType.LE:
                        return "<=";

                    case SqlNodeType.LongCount:
                        return "COUNT_BIG";

                    case SqlNodeType.LT:
                        return "<";

                    case SqlNodeType.GE:
                        return ">=";

                    case SqlNodeType.GT:
                        return ">";

                    case SqlNodeType.Max:
                        return "MAX";

                    case SqlNodeType.Min:
                        return "MIN";

                    case SqlNodeType.Mod:
                        return "%";

                    case SqlNodeType.Mul:
                        return "*";

                    case SqlNodeType.NE:
                        return "<>";

                    case SqlNodeType.Negate:
                        return "-";

                    case SqlNodeType.Not:
                        return "NOT";

                    case SqlNodeType.Or:
                        return "OR";

                    case SqlNodeType.Stddev:
                        return "STDEV";

                    case SqlNodeType.Sub:
                        return "-";

                    case SqlNodeType.Sum:
                        return "SUM";

                    default:
                        throw new Exception("Unknown operator: " + nodeType);
                }
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="node"></param>
            /// <returns></returns>
            internal virtual bool IsSimpleCrossJoinList(SqlNode node)
            {
                SqlJoin join = node as SqlJoin;
                if (null != join)
                {
                    if ((SqlJoinType.Cross == join.JoinType) && this.IsSimpleCrossJoinList(join.Left))
                    {
                        return this.IsSimpleCrossJoinList(join.Right);
                    }
                    return false;
                }
                SqlAlias alias = node as SqlAlias;
                if (null != alias)
                {
                    return (alias.Node is SqlTable);
                }
                return false;
            }

            /// <summary>
            /// 
            /// </summary>
            internal virtual void NewLine()
            {
                if (this._builder.Length > 0)
                {
                    this._builder.AppendLine();
                }
                for (int index = 0; index < this._depth; index++)
                {
                    this._builder.Append("    ");
                }
            }

            /// <summary>
            /// TODO
            /// </summary>
            /// <param name="joinType"></param>
            /// <returns></returns>
            internal bool RequiresOnCondition(SqlJoinType joinType)
            {
                switch (joinType)
                {
                    case SqlJoinType.Cross:
                    case SqlJoinType.CrossApply:
                    case SqlJoinType.OuterApply:
                        return false;

                    case SqlJoinType.Inner:
                    case SqlJoinType.LeftOuter:
                    case SqlJoinType.RightOuter:
                    case SqlJoinType.FullOuter:
                        return true;
                }
                throw new InvalidOperationException("Unknown join type");
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="node"></param>
            internal virtual void VisitCrossJoinList(SqlNode node)
            {
                SqlJoin join = node as SqlJoin;
                if (null != join)
                {
                    this.VisitCrossJoinList(join.Left);
                    this._builder.Append(", ");
                    this.VisitCrossJoinList(join.Right);
                }
                else
                {
                    Visit(node);
                }
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="source"></param>
            internal void VisitJoinSource(SqlSource source)
            {
                if (SqlNodeType.Join == source.NodeType)
                {
                    this._depth++;
                    this._builder.Append(", ");
                    Visit(source);
                    this._depth--;
                }
                else
                {
                    Visit(source);
                }
            }

            /// <summary>
            /// TODO
            /// </summary>
            /// <param name="node"></param>
            /// <param name="outer"></param>
            internal virtual void VisitWithParens(SqlNode node, SqlNode outer)
            {
                if (node == null)
                {
                    return;
                }
                SqlNodeType type1 = node.NodeType;
                if (type1 <= SqlNodeType.Literal)
                {
                    switch (type1)
                    {
                        case SqlNodeType.FunctionCall:
                        case SqlNodeType.TableValuedFunctionCall:
                        case SqlNodeType.Literal:
                        case SqlNodeType.ColumnRef:
                            goto Label_0091;

                        case SqlNodeType.Add:
                        case SqlNodeType.And:
                        case SqlNodeType.BitAnd:
                        case SqlNodeType.BitNot:
                        case SqlNodeType.BitOr:
                        case SqlNodeType.BitXor:
                            goto Label_009A;

                        case SqlNodeType.Alias:
                        case SqlNodeType.AliasRef:
                        case SqlNodeType.Assign:
                        case SqlNodeType.Avg:
                        case SqlNodeType.Between:
                            goto Label_00B1;
                    }
                    goto Label_00B1;
                }
                if (type1 <= SqlNodeType.Mul)
                {
                    switch (type1)
                    {
                        case SqlNodeType.Member:
                            goto Label_0091;

                        case SqlNodeType.Mul:
                            goto Label_009A;
                    }
                    goto Label_00B1;
                }
                switch (type1)
                {
                    case SqlNodeType.Not:
                    case SqlNodeType.Or:
                        goto Label_009A;

                    case SqlNodeType.Nop:
                        goto Label_00B1;

                    default:
                        if (type1 != SqlNodeType.Parameter)
                        {
                            goto Label_00B1;
                        }
                        goto Label_0091;
                }
                Label_0091:
                Visit(node);
                return;
                Label_009A:
                if (outer.NodeType == node.NodeType)
                {
                    Visit(node);
                    return;
                }
                Label_00B1:
                this._builder.Append("(");
                Visit(node);
                this._builder.Append(")");
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="name"></param>
            internal virtual void WriteName(string name)
            {
                if (name == null)
                {
                    name = "???";
                }
                this._builder.Append(name);
            }

            #endregion

            #region Private Implementation

            /// <summary>
            /// 
            /// </summary>
            /// <param name="type"></param>
            private void FormatType(ProviderType type)
            {
                this._builder.Append(type.ToQueryString());
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="expression"></param>
            /// <param name="definition"></param>
            /// <returns></returns>
            private string InferName(SqlExpression expression, string definition)
            {
                if (null == expression)
                {
                    return null;
                }
                SqlNodeType nodeType = expression.NodeType;
                switch (nodeType)
                {
                    case SqlNodeType.Column:
                        return ((SqlColumn) expression).Name;

                    case SqlNodeType.ColumnRef:
                        return ((SqlColumnRef) expression).Column.Name;

                    case SqlNodeType.ExprSet:
                        return this.InferName(((SqlExprSet) expression).Expressions[0], definition);
                }
                if (SqlNodeType.Member != nodeType)
                {
                    return definition;
                }
                return ((SqlMember) expression).Member.Name;
            }

            #endregion
        }

        #endregion
    }
}